//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.transaction.xaframework
{

	using XAException = javax.transaction.xa.XAException;

	using TransactionFailureException = org.neo4j.kernel.impl.transaction.TransactionFailureException;

///
/// <summary> * <CODE>XaTransaction</CODE> holds all the commands that participate in the
/// * transaction and then either rollbacks or commits them. Here are two example
/// * implementations:
/// * <p>
/// * 
/// * <pre>
/// * <CODE>
/// * // Example of XaTransaction implementation where commands are written to
/// * // to logical log directly when created
/// * public class MyTransaction extends XaTransaction
/// * {
/// *     private List cmds = new java.util.LinkedList();
/// * 
/// *     public boolean isReadyOnly()
/// *     {
/// *         return cmds.size() == 0;
/// *     }
/// * 
/// *     public void doAddCommand( XaCommand cmd )
/// *     {
/// *         cmds.add( cmd );
/// *     }
/// * 
/// *     public void doRollback()
/// *     {
/// *         IEnumerator  itr = cmds.iterator();
/// *         while ( itr.hasNext() )
/// *             ((XaCommand) itr.next()).rollback();
/// *     }
/// * 
/// *     public void doCommit()
/// *     {
/// *         IEnumerator  itr = cmds.iterator();
/// *         while ( itr.hasNext() )
/// *             ((XaCommand) itr.next()).execute();
/// *     }
/// * 
/// *     public void doPrepare()
/// *     {
/// *         // do nothing since commands are added before prepare
/// *     }
/// * }
/// * <CODE>
/// * </pre>
/// * 
/// * Some other implementation that makes use of prepare could look something like
/// * this:
/// * 
/// * <pre>
/// * <CODE>
/// * // Example of XaTransaction implementation where commands are written to
/// * // to logical log when transaction is prepared
/// * public class MyTransaction extends XaTransaction
/// * {
/// *     private List cmds = new java.util.LinkedList();
/// * 
/// *     public boolean isReadyOnly()
/// *     {
/// *         return cmds.size() == 0;
/// *     }
/// * 
/// *     public void doAddCommand( XaCommand cmd )
/// *     {
/// *         // do nothing, we call addCommand in prepare 
/// *     }
/// * 
/// *     public void doRollback()
/// *     {
/// *         IEnumerator  itr = cmds.iterator();
/// *         while ( itr.hasNext() )
/// *             ((XaCommand) itr.next()).rollback();
/// *     }
/// * 
/// *     public void doCommit()
/// *     {
/// *         IEnumerator  itr = cmds.iterator();
/// *         while ( itr.hasNext() )
/// *             ((XaCommand) itr.next()).execute();
/// *     }
/// * 
/// *     public void doPrepare()
/// *     {
/// *         IEnumerator  itr = cmds.iterator();
/// *         while ( itr.hasNext() )
/// *             addCommand( (XaCommand) itr.next() );
/// *     }
/// * 
/// * }
/// * </CODE>
/// * </pre> </summary>
/// 
	public abstract class XaTransaction
	{
///    
///     <summary> * Returns <CODE>true</CODE> if read only transaction, that is no
///     * modifications will be made once the transaction commits.
///     *  </summary>
///     * <returns> true if read only transaction </returns>
///     
		public abstract bool isReadOnly();

///    
///     <summary> * When a command is added to transaction it will be passed via this method.
///     * The <CODE>XaTransaction</CODE> needs to hold all the commands in memory
///     * until it receives the <CODE>doCommit</CODE> or <CODE>doRollback</CODE>
///     * call.
///     *  </summary>
///     * <param name="command">
///     *            The command to be added to transaction </param>
///     
		protected internal abstract void doAddCommand(XaCommand command);

///    
///     <summary> * Rollbacks the transaction, loop through all commands and invoke <CODE>rollback()</CODE>.
///     *  </summary>
///     * <exception cref="XAException">
///     *             If unable to rollback </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: protected abstract void doRollback() throws XAException;
		protected internal abstract void doRollback();

///    
///     <summary> * Called when transaction is beeing prepared.
///     *  </summary>
///     * <exception cref="XAException">
///     *             If unable to prepare </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: protected abstract void doPrepare() throws XAException;
		protected internal abstract void doPrepare();

///    
///     <summary> * Commits the transaction, loop through all commands and invoke 
///     * <CODE>execute()</CODE>.
///     *  </summary>
///     * <exception cref="XAEXception">
///     *             If unable to commit </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: protected abstract void doCommit() throws XAException;
		protected internal abstract void doCommit();

		private readonly int identifier;
		private readonly XaLogicalLog log;
		private bool isRecovered = false;
		private bool committed = false;
		private bool rolledback = false;
		private bool prepared = false;

		public XaTransaction(int identifier, XaLogicalLog log)
		{
			if (log == null)
			{
				throw new IllegalArgumentException("LogicalLog is null");
			}
			this.identifier = identifier;
			this.log = log;
		}

///    
///     <summary> * If this transacgtion is created during a recovery scan of the logical log
///     * method will be called to mark the transaction "recovered". </summary>
///     
		protected internal virtual void setRecovered()
		{
			isRecovered = true;
		}

///    
///     <summary> * Returns <CODE>true</CODE> if this is a "recovered transaction".
///     *  </summary>
///     * <returns> <CODE>true</CODE> if transaction was created during a recovery
///     *         else <CODE>false</CODE> is returned </returns>
///     
		public virtual bool isRecovered()
		{
			return isRecovered;
		}

///    
///     <summary> * Returns the "internal" identifier for this transaction. See
///     * <seealso cref="XaLogicalLog#getCurrentTxIdentifier"/>.
///     *  </summary>
///     * <returns> The transaction identifier </returns>
///     
		public int getIdentifier()
		{
			return identifier;
		}

///    
///     <summary> * Adds the command to transaction. First writes the command to the logical
///     * log then calls <seealso cref="#doAddCommand"/>. Also check
///     * <seealso cref="XaConnectionHelpImpl"/> class documentation example.
///     *  </summary>
///     * <param name="command">
///     *            The command to add to transaction </param>
///     * <exception cref="RuntimeException">
///     *             If problem writing command to logical log or this transaction
///     *             is committed or rolled back </exception>
///     
		public void addCommand(XaCommand command)
		{
			if (committed)
			{
				throw new TransactionFailureException("Cannot add command to committed transaction");
			}
			if (rolledback)
			{
				throw new TransactionFailureException("Cannot add command to rolled back transaction");
			}
			doAddCommand(command);
			try
			{
				log.writeCommand(command, identifier);
			}
			catch (IOException e)
			{
				throw new TransactionFailureException("Unable to write command to logical log.", e);
			}
		}

///    
///     <summary> * Used during recovery, calls <seealso cref="#doAddCommand"/>. Injects the command
///     * into the transaction without writing to the logical log.
///     *  </summary>
///     * <param name="command">
///     *            The command that will be injected </param>
///     
		protected internal virtual void injectCommand(XaCommand command)
		{
			doAddCommand(command);
		}

///    
///     <summary> * Rollbacks the transaction, calls <seealso cref="#doRollback"/>.
///     *  </summary>
///     * <exception cref="XAException">
///     *             If unable to rollback </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public final void rollback() throws XAException
		public void rollback()
		{
			if (committed)
			{
				throw new XAException("Cannot rollback partialy commited transaction. Recover and " + "commit");
			}
			rolledback = true;
			doRollback();
		}

///    
///     <summary> * Called before prepare marker is written to logical log. Calls
///     * <seealso cref="#doPrepare()"/>.
///     *  </summary>
///     * <exception cref="XAException">
///     *             if unable to prepare </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public final void prepare() throws XAException
		public void prepare()
		{
			if (committed)
			{
				throw new XAException("Cannot prepare comitted transaction");
			}
			if (rolledback)
			{
				throw new XAException("Cannot prepare rolled back transaction");
			}
			prepared = true;
			doPrepare();
		}

///    
///     <summary> * First registers the transaction identifier (see
///     * <seealso cref="XaLogicalLog#getCurrentTxIdentifier"/> then calls <seealso cref="#doCommit"/>.
///     *  </summary>
///     * <exception cref="XAException">
///     *             If unable to commit </exception>
///     
//JAVA TO VB & C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET:
//ORIGINAL LINE: public final void commit() throws XAException
		public void commit()
		{
			if (!prepared && !isRecovered())
			{
				throw new XAException("Cannot commit unprepared transaction");
			}
			log.registerTxIdentifier(getIdentifier());
			try
			{
				committed = true;
				doCommit();
			}
			finally
			{
				log.unregisterTxIdentifier();
			}
		}
	}
}